import Matrix from "./Matrix";
import Vector from "./Vector";

// 默认p为空时，返回各个坐标距离的最大值
function LpDistance(x1, x2, p){
  let Lp;
  if(!x1 || !x2)  return Lp;
  if(x1.length !== x2.length )  return Lp;
  
  if(!p){
    Lp = -1;
    for(let i=0, il=x1.length;i < il;i++){
      Lp = Math.max(Math.abs(x1[i]-x2[i]));
    }
    return Lp;
  }
  if(p < 1) return;
  Lp = 0;
  for(let i=0, il=x1.length;i < il;i++){
    let tempL = Math.abs(x1[i]-x2[i]);
    tempL = Math.pow(tempL, p);
    Lp += tempL;
  }
  Lp = Math.pow(Lp, 1/p);
  return Lp;
}

// module.exports={
//   LpDistance
// }

// 字符串转矩阵
function txtToMatrix(txt){
  let lines = txt.split(/\r\n|\n/);
  let matrix = [];
  for(let i = 0, il = lines.length; i < il; i++){
    lines[i] = lines[i].trim();
    if(lines[i].length <= 0) continue;
    matrix.push(lines[i].split(/\s+/));
  }
  return matrix;
}

function tile(mat, option){
  const repeatRow = option[0];
  const repeatCol = option[1];
  let newMat = new Matrix.Matrix();
  for(let r = 0; r < repeatRow; r++){
    newMat[r]=[];
    for(let c = 0; c < repeatCol; c++){
      newMat[r].push(...mat);
    }
  }
  return newMat;
}

// 归一化数据
function autoNorm(dataset){
  let minVals = dataset.min(0);
  let maxVals = dataset.max(0);
  let ranges  = Matrix.sub(maxVals, minVals);
  let normDataSet = new Matrix.Matrix();
  normDataSet.copyFrom(dataset);
  normDataSet = Matrix.sub(normDataSet, tile(minVals, [dataset.rows(), 1]));
  // console.log(minVals);
  normDataSet = Matrix.division(normDataSet, tile(ranges, [dataset.rows(), 1]));
  Matrix.toFixed(normDataSet);
  return normDataSet;
}

// knn算法-0
function classfy0(inX, dataset, labels, k){
  const rows        = dataset.rows();
  const diffMat     = Matrix.sub(tile(inX, [rows, 1]), dataset);
  const sqDiffMat   = Matrix.pow(diffMat, 2);
  const sqDistances = sqDiffMat.sum(1);
  const distances   = Matrix.pow(sqDistances, 0.5);
  const sortedDistIndicies = distances.argsort();

  let classCount={}
        
  for(let i = 0; i < k; i++){
    let voteIlabel = labels[sortedDistIndicies[i]];
    if(!classCount[voteIlabel]){
      classCount[voteIlabel] = 1;
    }
    else{
      classCount[voteIlabel] += 1;
    }
  }

  // 排序字典
  const tmpItems    = Object.keys(classCount).map((key)=>[key, classCount[key]]);
  const sortedClass = tmpItems.sort(([,a],[,b])=>b-a);
  
  return sortedClass[0][0];
}

function getKeyByValue(dict, value){
  for(let key in dict){
    if(dict[key] === value){
      return key;
    }
  }
  return undefined;
}

function imgToVector(txt){
  let lines = txt.split(/\r\n|\n/);
  let vect = new Vector();
  let height = 0;
  let width  = 0;
  for(let i=0, il=lines.length; i < il; i++){
    const line = lines[i].trim();
    if(i === 0) width = line.length;
    if(!line.length) continue;
    height ++;
    for(let j = 0, jl=line.length; j < jl; j++){
      vect.push(parseFloat(line[j]));
    }
  }
  return [vect, width, height];
}

// 加载数据集
// 输入：数据集多维列表，标签字典（可选）
// 数据集要求：
// 数据集为二维矩阵文本数据
// 前N-1列为特征，最后一列为标签；N为从列数
// 输出：特征矩阵，标签列表
function loadDataset(dat, labelDict={}){
  const dataset = {};
  dataset.Mat    = new Matrix.Matrix();
  dataset.Labels = new Matrix.Matrix();

  for(let i = 0, il = dat.length; i < il; i++){
    const label = dat[i][dat[i].length - 1];
    dataset.Mat.push(dat[i].slice(0, dat[i].length - 1));
    if(label in labelDict){
      dataset.Labels.push(labelDict[label]);
    }
    else{
      dataset.Labels.push(label);
    }
  }

  return dataset;
}

export {
  LpDistance,
  txtToMatrix,
  autoNorm,
  classfy0,
  getKeyByValue,
  imgToVector,
  loadDataset,
};
